#include "nsPerseusUtil.h"
#include "nsPerseusCode.h"

#include "nsISupports.h"
#include "nsStringAPI.h"

#include "math.h"

#include "prmem.h"
#include "prrng.h"
#include "prlong.h"
#include "prprf.h"


static void *
DefaultAllocTable(void *pool, PRSize size)
{
    return NS_Alloc(size);
}

static void
DefaultFreeTable(void *pool, void *item)
{
    NS_Free(item);
}

static PLHashEntry *
DefaultAllocEntry(void *pool, const void *key)
{
    return PR_NEW(PLHashEntry);
}

static void
PCCFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
{
    LOG(("[nsPerseusCode:PCCFreeEntry] : We delete a table entry!"));

    if (flag == HT_FREE_ENTRY) {
        char* entry = reinterpret_cast<char*>(he->value);
        free(entry);
        NS_Free(he);
    }
}


const static PLHashAllocOps HashAllocOps = 
{
    DefaultAllocTable, DefaultFreeTable,
    DefaultAllocEntry, PCCFreeEntry
};

nsPerseusCode::nsPerseusCode()
{
    LOG(("[Perseus Extension][nsPerseusCode::nsPerseusCode] \
            Hash Table is created"));
	mLock = PR_NewLock();
	if (mLock == NULL) {
		LOG(("[Perseus Extension][nsPerseusCode::nsPerseusCode] \
            Lock is created"));
	}

	PR_Lock(mLock);
    mTable = PL_NewHashTable(48, PL_HashString,
            PL_CompareStrings,PL_CompareStrings,&HashAllocOps,0);

	PR_Unlock(mLock);
}

nsPerseusCode::~nsPerseusCode()
{
    LOG(("[Perseus Extension][nsPerseusCode::~nsPerseusCode] \
            Detruction of the Hash Table"));
	PR_Lock(mLock);
	PL_HashTableDestroy(mTable);
	PR_Unlock(mLock);

	PR_DestroyLock(mLock);
}

/*
 * hasAcode return true, if host has already pcc parameters
 */
PRBool
nsPerseusCode::hasACode(const nsACString& host)
{
    LOG((" [Perseus Extension] - [nsPerseusCode::hasAcode] "));
    char * hostChar= ToNewCString(host);
    PRBool ok;
void * p;

	if (!hostChar) {
		ok = PR_FALSE;
        goto end;
	}
    PR_Lock(mLock);
	p = PL_HashTableLookup(mTable,hostChar);
	PR_Unlock(mLock);

	if (p) {
        ok = PR_TRUE;
	} else {  
        ok = PR_FALSE;
	}
	 
	NS_Free(hostChar);
end:
	return ok;
}

PRBool 
nsPerseusCode::deleteAcode(const nsACString &host)
{
    char * hostChar= ToNewCString(host);
    PRBool ok;

	if (!hostChar) {
        ok = PR_FALSE;
		goto end;
	}
	PR_Lock(mLock);
    ok=PL_HashTableRemove(mTable,hostChar);
	PR_Unlock(mLock);

	NS_Free(hostChar);
end:
	return ok;
}
char *
nsPerseusCode::getID(const nsACString &host)
{
    char * hostChar = ToNewCString(host);
	void * p = nsnull;
    char *tmppcc = nsnull;
    char * id = nsnull;
	
    if (!hostChar) {
        id = nsnull;
		goto end;
	}
    
    id=(char *) NS_Alloc(sizeof(char)*6);
    
    if (id == nsnull)
    {
        goto end1;
    }

	PR_Lock(mLock);
    p=PL_HashTableLookup(mTable,hostChar);
	PR_Unlock(mLock);

    LOG(("[Perseus Extension] - "
         "[nsPerseusCode::getID] GetID"));

    if (p) {
        tmppcc=( char *) p;
        memcpy(id,tmppcc,5);
        id[5]='\0';
    
		LOG(("[Perseus Extension] - "
			 "[nsPerseusCode::getID] PCC Code id: %s",id));
    }
	

end1:
	NS_Free(hostChar);
end:
    return id;
}

nsresult 
nsPerseusCode::createAcode(const nsACString &host)
{
    nsresult rv;
    PLHashEntry * entry;    
    unsigned int ID = rand()% (1<<16);
    char tid[6];

    if (hasACode(host)) {
        deleteAcode(host);
    } 
    
    itoa2(ID,tid,10);
    
    if (strlen(tid)<5)
    {
        memset(tid+strlen(tid),'1',6-strlen(tid));
        
    }
    LOG(("TID : %s",tid));
    pcc * code = generateCode();
    char * codeString = pcc2string(code);
    delete_code(code);
    char * toStore = (char *) NS_Alloc(sizeof(char)*(strlen(codeString)+5+1));
    memcpy(toStore,tid,5);
    memcpy((char *) (toStore+5),codeString,strlen(codeString));
    toStore[(strlen(codeString)+5)]='\0';
    free(codeString);
	char * hostChar= ToNewCString(host);
    
	if (!hostChar) {
        rv = NS_ERROR_FAILURE;
		goto end;
	}
    LOG((" [Perseus Extension] - [nsPerseusCode::createAcode] \
		 Add in Hash Table : %lu, host : %s",mTable,hostChar));

	
	PR_Lock(mLock);

    entry =PL_HashTableAdd(mTable,hostChar,toStore);

	PR_Unlock(mLock);

	if (!entry) {
        rv = NS_ERROR_FAILURE;
		goto end1;
	}
    
    rv = NS_OK;

end1:
	NS_Free(hostChar);
end:
	return rv;
}

char * 
nsPerseusCode::toString(const nsACString &host)
{

	LOG(("[Perseus Extension] - "
          "[PerseusCode::toString] Begin"));
    char * parameters = nsnull;
	char * hostChar= ToNewCString(host);
	void * p;
    char * tmppcc;

	if (!hostChar) {
        goto end;
	}

	LOG(("[Perseus Extension] - "
         " [PerseusCode::toString] search : %s in %lu",hostChar,mTable));
    
	PR_Lock(mLock);
	p=PL_HashTableLookup(mTable,hostChar);
	PR_Unlock(mLock);
	
	if (p) {
		LOG(("[Perseus Extension] - \
			 [nsPerseusCode::toString] A Value found for the key"));
		tmppcc=( char *) p;
        parameters = (char *) NS_Alloc(sizeof(char)*(strlen(tmppcc)-5+1));
        if (parameters == NULL)
            goto end1;
		memcpy(parameters,tmppcc+5,strlen(tmppcc)-5);
        parameters[strlen(tmppcc)-5]='\0';
		LOG(("[Perseus Extension] - "
			"[PerseusCode::toString] Parameters  : %s",parameters));
	}


end1:
	NS_Free(hostChar);
end:
	return parameters;

}

nsresult 
nsPerseusCode::code(const nsACString& host,
				    const nsACString & aData,
					PRUint64 aLength,
					nsACString & aDataCoded,
                    PERSEUS_CTYPE_t type)
{
    nsresult rv = NS_ERROR_FAILURE;
    LOG(("[Perseus Extension] - \
                [nsPerseusCode::code]"));
    char * hostChar= ToNewCString(host);
    void * p;
    char *tmppcc;
    char * dataCoded = NULL;
    pcc * code = NULL;

    if (!hostChar) {
        rv = NS_ERROR_FAILURE;
        goto end;
    }

    if (type == PERSEUS_UNSUPPORTED)
    {
        LOG(("[Perseus Extension] - "
                    "[nsPerseusCode::code] Perseus Content type unsupported"));
        rv = NS_ERROR_FAILURE;
        goto end1;
    }
    
    PR_Lock(mLock);

    p=PL_HashTableLookup(mTable,hostChar);

    PR_Unlock(mLock);

    LOG(("[Perseus Extension] - "
         "[nsPerseusCode::code] Get Value in hash table OK : Host : %s",hostChar));

    if(p) {
        
        LOG(("[Perseus Extension] - "
             "[nsPerseusCode::code] A Value found for the key"));
        tmppcc= (char *) p;
        
        code = parsePCC((tmppcc+5),(strlen(tmppcc)-5));
        if (code == NULL)
        {
            goto end;
        }
       
       switch(type)
       {
           case PERSEUS_MULTIPARTFORMDATA :
               {
                   LOG(("[Perseus Extension] - "
                        "[nsPerseusCode::code] Perseus Content type Multipart/form-data Encoding start"));
				   dataCoded = parseMultipart(ToNewCString(aData),aData.Length(),code,PERSEUS_CODE);
                   break;
               }
           case PERSEUS_URLENCODED :
               {
                   LOG(("[Perseus Extension] - "
                        "[nsPerseusCode::code] Perseus Content type application/x-www-form-urlencoded Encoding start"));
                   dataCoded = parseUrlEncoded(ToNewCString(aData),aData.Length(),code,PERSEUS_CODE);
                   break;

               }
           case PERSEUS_GET:
               {
                   LOG(("[Perseus Extension] - "
                               "[nsPerseusCode::code] HTTPs Method is GET - Encoding start"));
                   dataCoded = parseGet(ToNewCString(aData),aData.Length(),code,PERSEUS_CODE);
                   break;
               }
               case PERSEUS_UNSUPPORTED:
               {
                   goto end;
               }
        
       }
       
       delete_code(code);

       if (dataCoded == NULL)
       {
           LOG(("[Perseus Extension] - "
                "[nsPerseusCode::code] Perseus encoding error !!"));
           rv=NS_ERROR_FAILURE;
           goto end1;
       }
       aDataCoded.Assign(dataCoded);
    }
    else {
        LOG(("[Perseus Extension] - "
                    "[nsPerseusCode::code] No Value for this key : %s",hostChar));
        rv = NS_ERROR_FAILURE;
    }

    rv = NS_OK;
end1:
    NS_Free(hostChar);
end:
    return rv;
}

nsresult 
nsPerseusCode::decode(	const nsACString& host,
						const nsACString & aDataCoded,
							  PRUint64 aLength,
							  nsACString & aData)
{
	nsresult rv;  
	char * hostChar= ToNewCString(host);
    char *tmppcc = NULL;
	void * p;
	char * dataDeCoded=NULL;
	unsigned long dataDeCodedLength=0;
	unsigned long rest=0;
	pcc * code = NULL;

	if (!hostChar) {
		rv = NS_ERROR_FAILURE;
		goto end;
	}
    
	PR_Lock(mLock);
	p=PL_HashTableLookup(mTable,hostChar);
    PR_Unlock(mLock);

    if(p) {
        tmppcc=( char *) p;
        code = parsePCC(tmppcc+5,(strlen(tmppcc)-5));

        perseusHTTPdecode(code,ToNewCString(aDataCoded),aDataCoded.Length(),&dataDeCoded,&dataDeCodedLength, &rest);
        aData.Assign(dataDeCoded,dataDeCodedLength);
        delete_code(code);
        rv = NS_OK;
    }
	else {
		rv = NS_ERROR_FAILURE;
	}

	NS_Free(hostChar);
end:
	return rv;
}
